package com.sunday.common.statemachine.study.e12_Using_Extended_State;

import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.transition.Transition;

/**
 * <a href="https://docs.spring.io/spring-statemachine/docs/4.0.0/reference/index.html#sm-extendedstate">...</a>
 * <p>
 * StateMachineListenerAdapter 是 StateMachineListener 的一个适配器实现，它提供了默认的空实现，以便用户只需要重写自己关心的方法，而不需要实现所有的方法。这个类主要是用于监听状态机的各种事件，下面是每个方法的作用及其具体差异：
 * <p>
 * 1. stateChanged(State<S, E> from, State<S, E> to)
 * 作用：当状态机从一个状态 (from) 转换到另一个状态 (to) 时触发。这个方法会在状态发生变化时被调用，提供了状态机转换前后的状态信息。
 * 差异：通常用于监听状态的变化，适用于希望跟踪状态变化的场景。
 * 2. stateEntered(State<S, E> state)
 * 作用：当状态机进入一个新状态时触发。与 stateChanged 不同，它只在进入状态时触发，不关心状态的来源状态。
 * 差异：适用于需要在某个状态进入时执行某些操作（如日志记录、启动定时任务等）的场景。
 * 3. stateExited(State<S, E> state)
 * 作用：当状态机退出某个状态时触发。该方法仅在从某个状态离开时触发。
 * 差异：适用于需要在状态退出时执行某些操作（如清理资源、结束任务等）的场景。
 * 4. eventNotAccepted(Message<E> event)
 * 作用：当状态机收到一个事件，但该事件没有被接受时触发。事件可能由于状态不匹配或其它原因未能触发任何转换。
 * 差异：适用于捕获未处理的事件，通常可以用来记录日志或进行调试。
 * 5. transition(Transition<S, E> transition)
 * 作用：当状态机发生转换时触发。这个方法会在状态机进行状态转换时被调用，它提供了当前转换的详细信息。
 * 差异：用于详细的转换日志记录或在每次状态转换时进行一些自定义逻辑。
 * 6. transitionStarted(Transition<S, E> transition)
 * 作用：当状态机的转换开始时触发。它在转换发生之前调用，可以用来执行一些开始操作。
 * 差异：与 transition 方法不同，它仅在转换开始时被调用，适用于需要在转换开始时执行一些前置工作（如验证、日志记录等）的场景。
 * 7. transitionEnded(Transition<S, E> transition)
 * 作用：当状态机的转换结束时触发。它会在状态机转换完成后被调用，可以用来执行一些后置操作。
 * 差异：与 transitionStarted 不同，transitionEnded 在转换完成后被调用，适用于需要在状态转换完成后执行的操作（如更新状态、触发下一事件等）。
 * 8. stateMachineStarted(StateMachine<S, E> stateMachine)
 * 作用：当状态机启动时触发。通常在状态机初始化并开始处理事件时调用。
 * 差异：适用于在状态机启动时进行一些初始化工作，比如资源准备、日志记录等。
 * 9. stateMachineStopped(StateMachine<S, E> stateMachine)
 * 作用：当状态机停止时触发。状态机停止可能是由于处理完所有事件或发生错误时。
 * 差异：用于在状态机结束时进行清理操作，如关闭资源、保存数据等。
 * 10. stateMachineError(StateMachine<S, E> stateMachine, Exception exception)
 * 作用：当状态机发生错误时触发。它提供了发生的异常信息，通常用于捕获和记录状态机运行时的异常。
 * 差异：适用于错误处理，帮助开发人员在状态机异常时捕获并处理错误。
 * 11. extendedStateChanged(Object key, Object value)
 * 作用：当扩展状态发生变化时触发。extendedState 是一个可用于存储业务数据的容器，它允许您在状态机中存储和访问外部数据。
 * 差异：适用于监控扩展状态的变化，通常用于跨状态机事件的共享数据、处理动态业务数据等场景。
 * 12. stateContext(StateContext<S, E> stateContext)
 * 作用：该方法在每次状态机进行事件处理时触发，StateContext 提供了事件、状态、转换等的上下文信息。
 * 差异：与其他方法不同，这个方法提供了更详细的上下文信息，包括当前事件、状态、扩展状态等，适用于复杂的状态机操作和事件处理逻辑。
 * 总结
 * 这些方法提供了对状态机不同生命周期阶段的回调，可以帮助我们在状态变化、事件触发、错误发生等时刻执行特定的操作。通过使用 StateMachineListenerAdapter，开发者可以只实现他们需要关注的部分，而不必实现所有的方法。常见的用例包括：
 * <p>
 * 日志记录：在状态变更、转换开始或结束时记录日志。
 * 错误处理：在状态机出现错误时进行捕获和处理。
 * 扩展状态监控：监听扩展状态的变化并触发相应的业务操作。
 * 任务调度：在状态机启动或结束时启动或停止某些业务任务。
 */
@Slf4j
public class ExtendedStateVariableListener extends StateMachineListenerAdapter<OrderStates, OrderEvents> {

    /**
     * 特别要记住
     * 伪状态，基本上是不会显示的，并且不会参与到其中的打印
     */

    // 当状态机从一个状态 (from) 转换到另一个状态 (to) 时触发。这个方法会在状态发生变化时被调用，提供了状态机转换前后的状态信息。
    @Override
    public void stateChanged(State<OrderStates, OrderEvents> from, State<OrderStates, OrderEvents> to) {
        log.info("[StateMachineListenerAdapter][stateChanged] State change from {} to {}", from == null ? "无" : from.getId(), to.getId());
    }

    // 当状态机进入一个新状态时触发。与 stateChanged 不同，它只在进入状态时触发，不关心状态的来源状态
//    @Override
//    public void stateEntered(State<OrderStates, OrderEvents> state) {
//        log.info("[StateMachineListenerAdapter][stateEntered] State entered: {}", state.getId());
//    }

    // 当状态机退出某个状态时触发。该方法仅在从某个状态离开时触发。
//    @Override
//    public void stateExited(State<OrderStates, OrderEvents> state) {
//        log.info("[StateMachineListenerAdapter][stateExited] State exited: {}", state.getId());
//    }

    // 当状态机收到一个事件，但该事件没有被接受时触发。事件可能由于状态不匹配或其它原因未能触发任何转换。
//    @Override
//    public void eventNotAccepted(Message<OrderEvents> event) {
//        log.info("[StateMachineListenerAdapter][eventNotAccepted] Event not accepted: {}", event);
//    }

    // 当状态机发生转换时触发。这个方法会在状态机进行状态转换时被调用，它提供了当前转换的详细信息。
    @Override
    public void transition(Transition<OrderStates, OrderEvents> transition) {
        log.info("[StateMachineListenerAdapter][transition] Transition: {}", transition);
    }

    // 当状态机的转换开始时触发。它在转换发生之前调用，可以用来执行一些开始操作。
//    @Override
//    public void transitionStarted(Transition<OrderStates, OrderEvents> transition) {
//        log.info("[StateMachineListenerAdapter][transitionStarted] Transition started: {}", transition);
//    }

    // 当状态机的转换结束时触发。它会在状态机转换完成后被调用，可以用来执行一些后置操作。
//    @Override
//    public void transitionEnded(Transition<OrderStates, OrderEvents> transition) {
//        log.info("[StateMachineListenerAdapter][transitionEnded] Transition ended: {}", transition);
//    }

    // 当状态机启动时触发。通常在状态机初始化并开始处理事件时调用。
//    @Override
//    public void stateMachineStarted(StateMachine<OrderStates, OrderEvents> stateMachine) {
//        log.info("[StateMachineListenerAdapter][stateMachineStarted] State machine started: {}", stateMachine);
//    }

    // 当状态机停止时触发。状态机停止可能是由于处理完所有事件或发生错误时。
//    @Override
//    public void stateMachineStopped(StateMachine<OrderStates, OrderEvents> stateMachine) {
//        log.info("[StateMachineListenerAdapter][stateMachineStopped] State machine stopped: {}", stateMachine);
//    }

    @Override
    public void stateMachineError(StateMachine<OrderStates, OrderEvents> stateMachine, Exception exception) {
        log.info("[StateMachineListenerAdapter][stateMachineError] State machine error: {}", stateMachine, exception);
    }

    // 当扩展状态发生变化时触发。extendedState 是一个可用于存储业务数据的容器，它允许您在状态机中存储和访问外部数据。
    @Override
    public void extendedStateChanged(Object key, Object value) {
        // do something with changed variable
        log.info("[StateMachineListenerAdapter][extendedStateChanged] Extended state changed: {} -> {}", key, value);
    }

    // 该方法在每次状态机进行事件处理时触发，StateContext 提供了事件、状态、转换等的上下文信息。
//    @Override
//    public void stateContext(StateContext<OrderStates, OrderEvents> stateContext) {
//        log.info("[StateMachineListenerAdapter][stateContext] State context: {}", stateContext);
//    }
}